/*
 * @Author: Snow
 * @Date: 2021-10-21 15:47:51
 * @Description: 
 */
import { Matrix4 } from "../../utils/Matrix4";
import { Primitives } from "../../utils/Primitives";
import { TextrueUtil } from "../../utils/TextureUtil";
import { Util } from "../../utils/Util";
import { WebGLUtil } from "../../utils/WebGLUtil";
import { Render } from "../Render";
import { WebglTemplateBase } from "../template/WebglTemplateBase";

/*
 * @Author: Snow
 * @Date: 2021-10-11 20:35:00
 * @Description: 雾
 */
export class TextureFog extends WebglTemplateBase {

    protected override initUITools(): void {
        const gl = Render.GL;
        let translation = [200, 150];
        let scale = [1, 1];

        // Setup a ui.
        this.uiParam = {
            fogNear: 1,
            fogFar: 10,
            xOff: 1.1,
            zOff: 1.5
        };
        this.createSlider("fogNear", { value: this.uiParam.fogNear, slide: this.onUpateSlide(this, "fogNear"), min: 0, max: 1, step: 0.1, precision: 1 })
        this.createSlider("fogFar", { value: this.uiParam.fogFar, slide: this.onUpateSlide(this, "fogFar"), min: 0, max: 100, step: 0.1, precision: 1 })
        this.createSlider("xOff", { value: this.uiParam.xOff, slide: this.onUpateSlide(this, "xOff"), min: 1, max: 10, step: 0.1, precision: 1 })
        this.createSlider("zOff", { value: this.uiParam.zOff, slide: this.onUpateSlide(this, "zOff"), min: 1, max: 10, step: 0.1, precision: 1 })
    }

    protected override initProgram(): void {
        this.vsSource = `
            attribute vec4 a_position;
            attribute vec2 a_texcoord;
            
            uniform mat4 u_projection;
            uniform mat4 u_view;
            uniform mat4 u_world;
            
            varying vec2 v_texcoord;
            varying vec3 v_position;
            
            void main() {
                // Multiply the position by the matrix.
                gl_Position = u_projection * u_view * u_world *a_position;
                
                // Pass the texcoord to the fragment shader.
                v_texcoord = a_texcoord;
                // 传递相对于相机的负z位置
                // 相机看向-z方向，所以通常
                // 在相机前面的物体会有一个负Z位置
                // 取负我们得到一个正的深度
                v_position = (u_view * u_world * a_position).xyz;
            }
        `;

        this.fsSource = `
            precision mediump float;

            // Passed in from the vertex shader.
            varying vec2 v_texcoord;
            
            // The texture.
            uniform sampler2D u_texture;
            uniform vec4 u_fogColor;
            uniform float u_fogNear;
            uniform float u_fogFar;

            varying vec3 v_position;
            
            void main() {
                vec4 color = texture2D(u_texture, v_texcoord);
                float fogDepth = length(v_position);
                float fogAmount = smoothstep(u_fogNear,u_fogFar,fogDepth);
                // gl_FragCoord.z  0 到 1
                //float fogAmount = smoothstep(u_fogNear,u_fogFar,gl_FragCoord.z);
                gl_FragColor = mix(color,u_fogColor,fogAmount);
            }
        `;
        super.initProgram();
    }

    protected override initBuffers(): void {
        super.initBuffers();
        const gl = Render.GL;

        var positions = [
            -0.5, -0.5, -0.5,
            -0.5, 0.5, -0.5,
            0.5, -0.5, -0.5,
            -0.5, 0.5, -0.5,
            0.5, 0.5, -0.5,
            0.5, -0.5, -0.5,

            -0.5, -0.5, 0.5,
            0.5, -0.5, 0.5,
            -0.5, 0.5, 0.5,
            -0.5, 0.5, 0.5,
            0.5, -0.5, 0.5,
            0.5, 0.5, 0.5,

            -0.5, 0.5, -0.5,
            -0.5, 0.5, 0.5,
            0.5, 0.5, -0.5,
            -0.5, 0.5, 0.5,
            0.5, 0.5, 0.5,
            0.5, 0.5, -0.5,

            -0.5, -0.5, -0.5,
            0.5, -0.5, -0.5,
            -0.5, -0.5, 0.5,
            -0.5, -0.5, 0.5,
            0.5, -0.5, -0.5,
            0.5, -0.5, 0.5,

            -0.5, -0.5, -0.5,
            -0.5, -0.5, 0.5,
            -0.5, 0.5, -0.5,
            -0.5, -0.5, 0.5,
            -0.5, 0.5, 0.5,
            -0.5, 0.5, -0.5,

            0.5, -0.5, -0.5,
            0.5, 0.5, -0.5,
            0.5, -0.5, 0.5,
            0.5, -0.5, 0.5,
            0.5, 0.5, -0.5,
            0.5, 0.5, 0.5,

        ];

        // let tempMatrix = Matrix4.translation(-50, -75, -15);

        // for (let ii = 0; ii < positions.length; ii += 3) {
        //     var vector = Matrix4.transformPoint(tempMatrix, [positions[ii + 0], positions[ii + 1], positions[ii + 2], 1]);
        //     positions[ii + 0] = vector[0];
        //     positions[ii + 1] = vector[1];
        //     positions[ii + 2] = vector[2];
        // }

        let texcoords = [
            // select the top left image
            0, 0,
            0, 0.5,
            0.25, 0,
            0, 0.5,
            0.25, 0.5,
            0.25, 0,
            // select the top middle image
            0.25, 0,
            0.5, 0,
            0.25, 0.5,
            0.25, 0.5,
            0.5, 0,
            0.5, 0.5,
            // select to top right image
            0.5, 0,
            0.5, 0.5,
            0.75, 0,
            0.5, 0.5,
            0.75, 0.5,
            0.75, 0,
            // select the bottom left image
            0, 0.5,
            0.25, 0.5,
            0, 1,
            0, 1,
            0.25, 0.5,
            0.25, 1,
            // select the bottom middle image
            0.25, 0.5,
            0.25, 1,
            0.5, 0.5,
            0.25, 1,
            0.5, 1,
            0.5, 0.5,
            // select the bottom right image
            0.5, 0.5,
            0.75, 0.5,
            0.5, 1,
            0.5, 1,
            0.75, 0.5,
            0.75, 1,

        ];


        // an indexed quad
        let arrays = {
            position: { numComponents: 3, data: positions, },
            texcoord: { numComponents: 2, data: texcoords, },
        };


        let bufferInfo = WebGLUtil.createBufferInfoFromArrays(gl, arrays);

        let cameraAngleRadians = Util.degToRad(0);
        let fieldOfViewRadians = Util.degToRad(60);
        let cameraHeight = 50;

        let textures = [
            TextrueUtil.makeStripeTexture(gl, { color1: "#FFF", color2: "#CCC", }),
            TextrueUtil.makeCheckerTexture(gl, { color1: "#FFF", color2: "#CCC", }),
            TextrueUtil.makeCircleTexture(gl, { color1: "#FFF", color2: "#CCC", }),
        ];



        // Create a texture.
        let texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // Fill the texture with a 1x1 blue pixel.
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
            new Uint8Array([0, 0, 255, 255]));
        // Asynchronously load an image

        let image = new Image();
        //image.src = "f-texture.png";
        image.src = "noodles.jpg";
        image.addEventListener('load', function () {
            // Now that the image has loaded make copy it to the texture.
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            if (Util.isPowerOf2(image.width) && Util.isPowerOf2(image.height)) {
                gl.generateMipmap(gl.TEXTURE_2D);
            } else {
                // 不是 2 的幂，关闭贴图并设置包裹模式为到边缘
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            }

        });

        let fogColor = [0.8,0.9,1,1];
        let uniforms = {
            u_texture: texture,
            u_projection: null,
            u_view: null,
            u_world: null,
            u_fogColor: [1,1,1,1],
            u_fogNear: 1,
            u_fogFar: 5
        }


        let drawScene = () => {
            let time = this.ts;
            time = time;
            WebGLUtil.resizeCanvasToMatchDisplaySize(gl);
            // Clear the canvas AND the depth buffer.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            gl.clearColor(fogColor[0],fogColor[1],fogColor[2],fogColor[3]);
            // Compute the projection matrix
            let aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
            var projectionMatrix = Matrix4.perspective(fieldOfViewRadians, aspect, 1, 2000);

            // Compute the camera's matrix using look at.
            let cameraPosition = [0, 0, 3];
            let target = [0, 0, 0];
            let up = [0, 1, 0];
            let cameraMatrix = Matrix4.lookAt(cameraPosition, target, up);

            // Make a view matrix from the camera matrix.
            let viewMatrix = Matrix4.invert(cameraMatrix);

            let viewProjectionMatrix = Matrix4.multiply(projectionMatrix, viewMatrix);

            let sphereXRotation = time;
            let sphereYRotation = time;
            let cubeXRotation = -time;
            let cubeYRotation = time;
            let coneXRotation = time;
            let coneYRotation = -time;

            gl.useProgram(this.programInfo.program);



            let rotaion = (xRotation, yRotation): number[] => {
                let matrix = Matrix4.rotateX(xRotation);
                return Matrix4.multiply(matrix, Matrix4.rotateY(yRotation));
            }

            // Setup all the needed buffers and attributes.
            WebGLUtil.setBuffersAndAttributes(gl, this.programInfo, bufferInfo);
            let numCube = 40;
            for (let index = 0; index < numCube; index++) {
                uniforms.u_projection = projectionMatrix;
                uniforms.u_view = viewMatrix;

                let mat = Matrix4.translation(-2 + index * this.uiParam.xOff, 0, -index * this.uiParam.zOff);
                //let mat = Matrix4.identity();
                uniforms.u_world = Matrix4.multiply(mat, rotaion(sphereXRotation, sphereYRotation));
                //uniforms.u_world = rotaion(sphereXRotation, sphereYRotation);
                uniforms.u_fogColor = fogColor;
                uniforms.u_fogFar = this.uiParam.fogFar;
                uniforms.u_fogNear = this.uiParam.fogNear;

                // Set the uniforms that are the same for all objects.

                WebGLUtil.setUniforms(this.programInfo, uniforms);

                // Draw the geometry.
                gl.drawArrays(gl.TRIANGLES, 0, bufferInfo.numElements);

            }

        }
        this.draw = drawScene;
    }
}